列表組件已經完成,再來可以做用戶頁面的文章列表了。
首先先修改用戶頁面的 Controller 部分,因為之後這個頁面還要放用戶喜歡的文章的列表,所以把標題 pageTitle
提出來。然後在 UserPresenter
後追加 posts
和 postsCount
兩個欄位:
use App\Presenters\PostPresenter;
public function index(User $user)
{
return Inertia::render('User/Profile', [
'pageTitle' => "$user->name 的文章",
'type' => 'show',
'user' => UserPresenter::make($user)->with(fn (User $user) => [
'posts' => PostPresenter::collection(
$user->posts()
->where('published', true)
->latest()
->paginate()
)->preset('list'),
'postsCount' => $user->publishedPosts()->count(),
])->get(),
]);
}
還有修改視圖頁面,增加切換頁面用的頁籤 <tabs>
,和文章列表組件 <post-list>
:
resources/js/Pages/User/Profile.vue
<template>
<div class="py-6 md:py-8">
<div class="card card-main">
<div class="md:flex">
...
<div class="mt-4 md:mt-0 md:ml-6 text-center md:text-left">
...
<tabs class="mt-4 justify-center md:justify-start" :active="type">
<tab name="show" :url="`/user/${user.id}`">
<icon icon="heroicons-outline:book-open" />文章 {{ user.postsCount }}
</tab>
</tabs>
</div>
</div>
</div>
<div class="card card-main mt-6">
<post-list :posts="user.posts" />
</div>
</div>
</template>
<script>
import AppLayout from '@/Layouts/AppLayout'
import PostList from '@/Lightning/PostList'
import Tabs from '@/Components/Tabs'
import Tab from '@/Components/Tab'
export default {
layout: AppLayout,
metaInfo() {
return {
title: this.pageTitle
}
},
components: {
PostList,
Tabs,
Tab
},
props: {
pageTitle: String,
type: String,
user: Object
}
}
</script>
看到的依然是假文產生的,別在意...
嗯!頁面都正常了。但,這裡有一點點嚴重的資料庫查詢問題。
頭號效能殺手就是 N+1 query 問題!很容易在 Laravel 裡出現。
用過 Laravel 的都知道可以用 預加載 (Eager Loading) 來解此問題。為了證明有 N+1 問題,現在先裝 Laravel Debugbar 來確認:
composer require barryvdh/laravel-debugbar --dev
php artisan vendor:publish --provider="Barryvdh\Debugbar\ServiceProvider"
裝好後再打開用戶頁面,選擇 Queries
頁籤,你就會看到多個 select * from `users` where `users`.`id` = 1 limit 1
,這就是 N+1 問題:
解法是在加載文章時 ($user->posts()
) 先預加載每個文章的作者 (->with('author')
):
app/Http/Controllers/User/ProfileController.php
public function index(User $user)
{
return Inertia::render('User/Profile', [
...
'user' => UserPresenter::make($user)->with(fn (User $user) => [
'posts' => PostPresenter::collection(
$user->posts()
->with('author')
->where('published', true)
->latest()
->paginate()
)->preset('list'),
...
])->get(),
]);
}
優化之後 Query 次數瞬間減少許多,但還有一個點可以優化:
其實關聯查詢總數量也可以預加載,使用 loadCount('publishedPosts')
加載,然後它會把加載到的數量值丟到 $user->published_posts_count
裡,參考 Counting Related Models:
app/Http/Controllers/User/ProfileController.php
public function index(User $user)
{
$user->loadCount('publishedPosts');
return Inertia::render('User/Profile', [
...
'user' => UserPresenter::make($user)->with(fn (User $user) => [
...
'postsCount' => $user->published_posts_count,
])->get(),
]);
}
完成!沒有重複的查詢了!
現在終於要做 Lightning 首頁囉!首頁不放什麼花俏的東西,就放全部文章的列表。新增一個 ShowPostList
Controller:
php artisan make:controller Post/ShowPostList -i
範例的兩個路由可以刪掉了。然後新增首頁路由:
routes/web.php
// Posts
Route::get('/', 'Post\ShowPostList');
...
列表的頁面前面都做過了,搬來用改一改就可以用。跟上面一樣,這裡也要用 with('author')
預加載作者的 Model:
app/Http/Controllers/Post/ShowPostList.php
use App\Post;
use App\Presenters\PostPresenter;
use Inertia\Inertia;
public function __invoke()
{
$posts = Post::with('author')
->where('published', true)
->latest()
->paginate();
return Inertia::render('Home', [
'posts' => PostPresenter::collection($posts)
->preset('list')
->get(),
]);
}
之前當範例的頁面 HelloWorld.vue
和 About.vue
也可以刪掉,然後新增首頁的頁面:
resources/js/Pages/Home.vue
<template>
<div class="py-6 md:py-8">
<alert v-if="$page.flash.success" class="shadow mb-6">{{ $page.flash.success }}</alert>
<div class="card card-main">
<post-list :posts="posts" />
</div>
</div>
</template>
<script>
import AppLayout from '@/Layouts/AppLayout'
import PostList from '@/Lightning/PostList'
import Alert from '@/Components/Alert'
export default {
layout: AppLayout,
components: {
Alert,
PostList
},
props: {
posts: Object
}
}
</script>
這樣,就有了一個簡單的首頁啦!
本篇除了補上首頁和用戶頁面的文章列表外,還簡單優化了資料庫查詢。下篇要做文章的編輯和刪除,基本文章功能也快完成囉!
Lightning 範例程式碼:https://github.com/ycs77/lightning